%%% ------------------------------------------------------------------ 
%%% Licensed under the Apache License, Version 2.0 (the 'License');
%%%  you may not use this file except in compliance with the License.
%%%  You may obtain a copy of the License at
%%%
%%%      http://www.apache.org/licenses/LICENSE-2.0
%%%
%%% Copyright (c) 2017 dwg <bypf2009@vip.qq.com>
%%%
%%%  Unless required by applicable law or agreed to in writing, software
%%%  distributed under the License is distributed on an 'AS IS' BASIS,
%%%  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%%%  See the License for the specific language governing permissions and
%%%  limitations under the License.
%%%
%%% @doc  uri
%%% @author  dwg <'bypf2009@vip.qq.com'> 
%%% @copyright 2017 dwg <bypf2009@vip.qq.com>
%%% @reference http://stackoverflow.com/questions/114196/url-encode-in-erlang
%%% @reference https://gist.github.com/renatoalbano/3796470
%%% @end
%%% created|changed : 2017-04-24 14:02
%%% coding : utf-8 
%%% ------------------------------------------------------------------ 
-module(ali_uri).
-author("qingchuwudi").

-export([encode/1, to_uri/1, to_utf8/1]).

%%@doc 参数编码为urlunicode
-spec encode(S) -> URI when
        S   :: iodata(),
        URI :: string().
encode(S) when is_list(S) ->
    encode(unicode:characters_to_binary(S));
encode(<<C:8, Cs/binary>>) when C >= $a, C =< $z ->
    [C] ++ encode(Cs);
encode(<<C:8, Cs/binary>>) when C >= $A, C =< $Z ->
    [C] ++ encode(Cs);
encode(<<C:8, Cs/binary>>) when C >= $0, C =< $9 ->
    [C] ++ encode(Cs);
encode(<<C:8, Cs/binary>>) when C == $. ->
    [C] ++ encode(Cs);
encode(<<C:8, Cs/binary>>) when C == $- ->
    [C] ++ encode(Cs);
encode(<<C:8, Cs/binary>>) when C == $_ ->
    [C] ++ encode(Cs);
encode(<<C:8, Cs/binary>>) when C == $~ -> % for Alibaba
    [C] ++ encode(Cs);
encode(<<C:8, Cs/binary>>) ->
    escape_byte2(C) ++ encode(Cs);
encode(<<>>) ->
    "".

escape_byte2(C) ->
    "%" ++ hex_octet2(C).

hex_octet2(N) when N =< 9 ->
    [$0 + N];
hex_octet2(N) when N > 15 ->
    hex_octet2(N bsr 4) ++ hex_octet2(N band 15);
hex_octet2(N) ->
    [N - 10 + $A].


%%% 
%%% ParamsExtra : encode to utf8
%%%

%%@doc map/list 格式的http参数表转换为 uri
-spec to_uri(Params) -> URI when
        Params :: pub_params:params(),
        URI :: string().
to_uri(Params) when is_map(Params) ->
    UriList = lists:sort([encode(K,V) ||{K,V}<-maps:to_list(Params)]),
    list2uri(UriList);
to_uri(Params) when is_list(Params) ->
    UriList = lists:sort([encode(K,V) ||{K,V}<-Params]),
    list2uri(UriList).

encode(K, V) ->
    {encode(K), encode(V)}.

%%%
%%% proplist to uri struct
%%%

list2uri([]) ->
    "";
list2uri(List) when is_list(List) ->
    List2 = lists:reverse(List),
    list2uri(List2, []);
list2uri(Map) when is_map(Map) ->
    list2uri(maps:to_list(Map)).

list2uri([{Key, Val} | []], Url) ->
    list2uri([], [Key, <<"=">> , Val | Url]);
list2uri([{Key, Val} | Rest], Url) ->
    list2uri(Rest, [<<"&">>, Key, <<"=">> , Val | Url]);
list2uri([], Url) ->
    binary:list_to_bin(Url).
    

%%@doc 转为utf8格式的二进制
-spec to_utf8(Params) -> ParamsRes when
        Params :: pub_params:params(),
        ParamsRes :: pub_params:params().
to_utf8(Params) when is_map(Params) ->
    maps:from_list([to_utf8(K,V) ||{K,V}<-maps:to_list(Params)]);
to_utf8(Params) when is_list(Params) ->
    lists:sort([to_utf8(K,V) ||{K,V}<-Params]).

to_utf8(K, V) when (is_list(K) orelse is_binary(K))
            andalso(is_list(V) orelse is_binary(V)) ->
    {unicode:characters_to_binary(K), 
     unicode:characters_to_binary(V)};

to_utf8(K, V) ->
    K1 = if
        is_list(K) orelse is_binary(K) -> 
            K;
        true -> 
            tools:term_to_string(K)
    end,
    V1 = if
        is_list(V) orelse is_binary(V) -> 
            V;
        true -> 
            tools:term_to_string(V)
    end,
    to_utf8(K1, V1).
